package ligne2vie.topos.client.process;

import ligne2vie.topos.client.asynccalls.CreateAndDisplaySummit;
import ligne2vie.topos.client.forms.SummitEditForm;
import ligne2vie.topos.client.forms.SummitEditFormDraggableSummitEventCallBack;
import ligne2vie.topos.client.maps.MyMapWidget;
import ligne2vie.topos.client.panels.ActionProcessPanel;
import ligne2vie.topos.client.service.SummitService;
import ligne2vie.topos.client.service.SummitServiceAsync;
import ligne2vie.topos.shared.bo.TSummit;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.maps.client.event.HasMouseEvent;
import com.google.gwt.maps.client.event.MouseEventCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * The class {@link SummitAddProcess} allows reacting on a single-left-button on the map, in order to add a summit where
 * the map has been clicked. The process then displays a form allowing user to add complementary information about the
 * summit, which is then saved into the database.
 * 
 * @author thuguerre
 * @since 1.0.0
 */
public class SummitAddProcess {

	/**
	 * Default constructor. Prepares map before allowing user to click on it.
	 * 
	 * @param actionProcessPanel
	 *            Panel to use to display form.
	 */
	public SummitAddProcess() {

		ActionProcessPanel.getInstance().init("Ajouter un sommet");

		// --- creating process steps

		FirstStep firstStep = new FirstStep();
		SecondStep secondStep = new SecondStep();

		// --- preparing map

		MyMapWidget.getInstance().hideSummits(null);
		MyMapWidget.getInstance().addLeftClickListener(new FirstStepLeftClick(secondStep));

		// --- starting action

		firstStep.start();
	}

	/**
	 * Summit creation process first step : click on the map to locate the summit. Display the help message asking the
	 * user to click on the map. The action process can be canceled by clicking on corresponding button.
	 * 
	 * @author thuguerre
	 * @since 1.0.0
	 */
	private class FirstStep extends VerticalPanel implements ProcessAction {

		/**
		 * Default constructor.
		 * 
		 * @param actionProcessPanel
		 *            Panel to use to display form.
		 */
		protected FirstStep() {

			// --- preparing form displaying help information

			this.add(new HTML(
					"<p>Pour ajouter un nouveau sommet, cliquez sur la carte &agrave; l'endroit exact de sa position.</p"));
			this.setHorizontalAlignment(ALIGN_RIGHT);
			this.add(new CancelButton());

			// --- adding displaying form to panel

			ActionProcessPanel.getInstance().addStackWidget(this, "1. Localisation du sommet");
		}

		/**
		 * {@inheritDoc}
		 */
		public void start() {
			ActionProcessPanel.getInstance().startAction();
		}

		/**
		 * {@inheritDoc}
		 */
		public void end() {
			// --- do nothing
		}

		/**
		 * {@inheritDoc}
		 */
		public void focus() {
			// --- nothing to focus on this form
		}
	}

	/**
	 * Summit creation process second step : when the summit has been clicked, this step displays the form allowing user
	 * to fill main required information about the summit. The action process can be canceled by clicking on
	 * corresponding button. When the OK button is clicked, the form is validated and, if information are correct, they
	 * are transfered to the remote service to save the summit into the database. The process is then ended.
	 * 
	 * @author thuguerre
	 * @since 1.0.0
	 */
	private class SecondStep implements ProcessAction {

		private SummitEditForm form;
		private OKButton okButton;
		private CancelButton cancelButton;

		/**
		 * Default constructor.
		 * 
		 * @param actionProcessPanel
		 *            Panel to use to display form.
		 */
		protected SecondStep() {

			this.okButton = new OKButton(this);
			this.cancelButton = new CancelButton();
			this.form = new SummitEditForm(okButton, cancelButton);

			ActionProcessPanel.getInstance().addStackWidget(form, "2. Informations Principales");
		}

		protected SummitEditForm getForm() {
			return form;
		}

		protected void setTemporaryAddedSummit(TSummit temporaryAddedSummit) {
			this.okButton.setTemporaryAddedSummit(temporaryAddedSummit);
			this.cancelButton.setTemporaryAddedSummit(temporaryAddedSummit);
		}

		/**
		 * {@inheritDoc}
		 */
		public void start() {
			ActionProcessPanel.getInstance().enableStep(1);
			this.focus();
		}

		/**
		 * {@inheritDoc}
		 */
		public void focus() {
			this.form.focus();
		}

		/**
		 * {@inheritDoc}
		 */
		public void end() {
			// --- do nothing
		}
	}

	/**
	 * Initial click on the map, locating the summit and launching the rest of the process. This event should be added
	 * as a listener on the map where the user has to click.
	 * 
	 * @author thuguerre
	 * @since 1.0.0
	 * @see FirstStep
	 */
	private class FirstStepLeftClick extends MouseEventCallback {

		private SecondStep secondStep;

		/**
		 * Default constructor.
		 * 
		 * @param actionProcessPanel
		 *            Panel to use to display form.
		 */
		protected FirstStepLeftClick(SecondStep secondStep) {
			this.secondStep = secondStep;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void callback(HasMouseEvent event) {

			// --- creating and displaying summit corresponding to received coordinates

			TSummit summit = new TSummit(-1, "", 0, event.getLatLng().getLatitude(), event.getLatLng().getLongitude(),
					"");

			MyMapWidget.getInstance().setSummitDraggable(summit, true,
					new SummitEditFormDraggableSummitEventCallBack(secondStep.getForm()));

			// --- filling the form with obtained information

			secondStep.setTemporaryAddedSummit(summit);
			secondStep.getForm().setLatitude(event.getLatLng().getLatitude());
			secondStep.getForm().setLongitude(event.getLatLng().getLongitude());

			secondStep.start();
		}
	}

	/**
	 * Defining the behavior of the process when clicking the cancel button.
	 * 
	 * @author thuguerre
	 * @since 1.0.0
	 */
	private class CancelButton extends Button implements ClickHandler {

		private TSummit temporaryAddedSummit;

		/**
		 * Default constructor.
		 * 
		 * @param actionProcessPanel
		 *            Panel to use to display form.
		 */
		public CancelButton() {

			// --- Configuring the cancel button

			this.setText("Annuler");
			this.addClickHandler(this);
		}

		protected void setTemporaryAddedSummit(TSummit temporaryAddedSummit) {
			this.temporaryAddedSummit = temporaryAddedSummit;
		}

		/**
		 * {@inheritDoc}
		 */
		public void onClick(ClickEvent event) {

			if (this.temporaryAddedSummit != null) {
				MyMapWidget.getInstance().removeSummit(this.temporaryAddedSummit);
			}

			MyMapWidget.getInstance().reInitListeners();
			MyMapWidget.getInstance().showSummits();

			ActionProcessPanel.getInstance().stopAction();
		}
	}

	/**
	 * Defining the behavior of the {@link AddSummitForm} when clicking the OK button.
	 * 
	 * @author thuguerre
	 * @since 1.0.0
	 */
	private class OKButton extends Button implements ClickHandler {

		private SecondStep secondStep;
		private TSummit temporaryAddedSummit;

		/**
		 * Default constructor.
		 * 
		 * @param actionProcessPanel
		 *            Panel to clean.
		 * @param secondStep
		 *            Step containing information to validate.
		 */
		public OKButton(SecondStep secondStep) {

			this.secondStep = secondStep;

			// --- Configuring the ok button

			this.setText("OK");
			this.addClickHandler(this);
		}

		protected void setTemporaryAddedSummit(TSummit temporaryAddedSummit) {
			this.temporaryAddedSummit = temporaryAddedSummit;
		}

		/**
		 * {@inheritDoc}
		 */
		public void onClick(ClickEvent event) {

			if (!secondStep.getForm().validate()) {
				return;
			}

			// --- removing temporary summit
			MyMapWidget.getInstance().removeSummit(this.temporaryAddedSummit);

			// --- displaying all presents summits
			MyMapWidget.getInstance().showSummits();

			// --- closing the process
			MyMapWidget.getInstance().reInitListeners();
			ActionProcessPanel.getInstance().stopAction();

			// --- preparing creation on server side
			final SummitServiceAsync summitService = GWT.create(SummitService.class);
			CreateAndDisplaySummit result = new CreateAndDisplaySummit();

			// --- creating the summit into the database
			summitService.createSummit(secondStep.getForm().getName(), secondStep.getForm().getAltitude(), secondStep
					.getForm().getLatitude(), secondStep.getForm().getLongitude(), secondStep.getForm()
					.getDescription(), result);
		}
	}
}
